const API = {};

//生成num个0的字符串
function _makeZero(num) {
  var str = "";
  for (var i = 0; i < num; i++) {
    str += "0";
  }
  return str;
}

/**
 * 判断是否为整数，字符整数也返回true
 *
 * @param num
 * @returns
 */
function isInteger(num) {
  return Math.floor(num) === Number(num);
}

/**
 * 将数值转为字符串
 *
 * 通过移动小数点  扩大倍数或缩小倍数(解决出现e+、e-的问题)
 *
 * JavaScript在以下情景会自动将数值转换为科学计数法：
 *   1）小数点前的数字多于21位。
 *   2）小数点后的零多于5个
 */
function scienceNum(value) {
  if (!value) {
    return value;
  }
  if (typeof value === "number") {
    value = value + "";
  }
  var eIndex = value.indexOf("E");
  if (eIndex == -1) {
    eIndex = value.indexOf("e");
  }
  if (eIndex != -1) {
    var doubleStr = value.substring(0, eIndex); //e前面的值
    var eStr = parseInt(value.substring(eIndex + 1, value.length)); //e后面的值
    var doubleStrArr = doubleStr.split(".");
    var doubleStr1 = doubleStrArr[0] || "";
    var doubleStr2 = doubleStrArr[1] || "";

    if (eStr < 0) {
      //e- 很小的数
      var str1Len = doubleStr1.length;
      var eStrs = Math.abs(eStr);
      if (str1Len > eStrs) {
        var nums = doubleStr1.substring(0, eStrs);
        var nume = doubleStr1.substring(eStrs, str1Len);
        doubleStr = nums + "." + nume + nume;
      } else if (str1Len < eStrs) {
        var indexNum = eStrs - str1Len;
        var str = _makeZero(indexNum); //用0补齐
        doubleStr = "0." + str + doubleStr1 + doubleStr2;
      } else {
        doubleStr = "0." + doubleStr1 + doubleStr2;
      }
    } else {
      //e+ 很大的数
      var str2Len = doubleStr2.length;
      if (str2Len > eStr) {
        var _nums = doubleStr2.substring(0, eStr);
        var _nume = doubleStr2.substring(eStr, str2Len);
        doubleStr = doubleStr1 + _nums + "." + _nume;
      } else if (str2Len < eStr) {
        var _indexNum = eStr - str2Len;
        var _str = _makeZero(_indexNum); //用0补齐
        doubleStr = doubleStr1 + doubleStr2 + _str;
      } else {
        doubleStr = doubleStr1 + doubleStr2;
      }
    }
    value = doubleStr;
  }
  return value;
}

/**
 * 将数值升级(10的X的次方)到整数
 */
function science(num) {
  var re = {
    r1: 0, //数字去掉小数点后的值，也就是 r1*r2 的结果
    r2: 1, //小数部分，10的长度次幂
  };
  if (isInteger(num)) {
    //整数直接返回
    re.r1 = num;
    return re;
  }
  var snum = scienceNum(num + ""); //处理0.123e-10类似问题
  var dotPos = snum.indexOf("."); //小数点位置
  var len = snum.substr(dotPos + 1).length; //小数点长度
  re.r2 = Math.pow(10, len);
  re.r1 = parseInt(snum.replace(".", ""));
  return re;
}

/**
 * 默认toFixed方法为四舍六入五成双算法
 * 重写toFixed方法调整为四舍五入算法
 */
function toFixed(value, d) {
  var s = value + "";
  if (!d) d = 0;
  if (typeof d == "string") {
    d = Number(d);
  }
  if (s.indexOf(".") == -1) {
    s += ".";
  }
  s = scienceNum(s); //处理e+、e-情况
  s += new Array(d + 1).join("0");
  if (new RegExp("^(-|\\+)?(\\d+(\\.\\d{0," + (d + 1) + "})?)\\d*$").test(s)) {
    var _s = "0" + RegExp.$2,
      pm = RegExp.$1,
      a = RegExp.$3.length,
      b = true;
    if (a == d + 2) {
      a = _s.match(/\d/g);
      if (parseInt(a[a.length - 1]) > 4) {
        for (var i = a.length - 2; i >= 0; i--) {
          a[i] = parseInt(a[i]) + 1;
          if (a[i] == 10) {
            a[i] = 0;
            b = i != 1;
          } else break;
        }
      }
      _s = a
        .join("")
        .replace(new RegExp("(\\d+)(\\d{" + d + "})\\d$"), "$1.$2");
    }
    if (b) {
      _s = _s.substr(1);
    }
    return (pm + _s).replace(/\.$/, "");
  }
  return value + "";
}

/**
 * 四则运算
 *
 * @param x
 * @param y
 * @param op 操作符，0：加；1：减；2：乘；3：除
 * @param acc 保留小数位个数，进行四舍五入
 */
function execute(x, y, op, acc) {
  var xx = Number(x == undefined ? 0 : x);
  var yy = Number(y == undefined ? 0 : y);

  //
  var a = science(xx);
  var b = science(yy);

  var na = a.r1;
  var nb = b.r1;

  var ta = a.r2;
  var tb = b.r2;
  var maxt = Math.max(ta, tb);

  //精度值处理
  var result = 0;
  switch (parseInt(op, 10)) {
    case 0: //加
      result = (xx * maxt + yy * maxt) / maxt;
      break;
    case 1: //减
      result = (xx * maxt - yy * maxt) / maxt;
      break;
    case 2: // 乘
      result = (na * nb) / (ta * tb);
      break;
    case 3: // 除
      result = (na / nb) * (tb / ta);
    default:
  }

  //小数位数处理
  if (acc) {
    return toFixed(result, acc);
  } else {
    return Number(result);
  }
}

//加法运算
API.add = function (x, y, acc) {
  return execute(x, y, 0, acc);
};

//减法运算
API.subtract = function (x, y, acc) {
  return execute(x, y, 1, acc);
};

//乘法运算
API.multiply = function (x, y, acc) {
  return execute(x, y, 2, acc);
};

//除法运算
API.divide = function (x, y, acc) {
  return execute(x, y, 3, acc);
};

module.exports = API;
